package fr.umlv.m2.jee.spij.service.course.impl;

import fr.umlv.m2.jee.spij.persistence.education.Unit;
import fr.umlv.m2.jee.spij.persistence.education.dao.HibernateUnitDAO;
import fr.umlv.m2.jee.spij.persistence.schooling.Course;
import fr.umlv.m2.jee.spij.persistence.schooling.Training;
import fr.umlv.m2.jee.spij.persistence.schooling.dao.HibernateCourseDAO;
import fr.umlv.m2.jee.spij.persistence.schooling.dao.HibernateTrainingDAO;
import fr.umlv.m2.jee.spij.service.course.DisplayCourse;
import fr.umlv.m2.jee.spij.service.course.IDisplayCourseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Provides default implementation of {@code IDisplayCourseService}.
 *
 * @author lounes
 */
@Service("defaultDisplayCourseService")
public class DefaultDisplayCourseService implements IDisplayCourseService {

    /**
     * the course DAO
     */
    @Autowired
    private HibernateCourseDAO courseDAO;

    /**
     * the training DAO
     */
    @Autowired
    private HibernateTrainingDAO trainingDAO;
    /**
     * the unit DAO
     */
    @Autowired
    private HibernateUnitDAO unitDAO;

    /**
     * @see fr.umlv.m2.jee.spij.service.course.IDisplayCourseService#getAllDisplayCourses(MessageContext)
     */
    @Transactional(readOnly = true)
    @Override
    public List<DisplayCourse> getAllDisplayCourses(MessageContext context) {
        final List<Course> courseList = courseDAO.findAll();
        final List<DisplayCourse> displayCourseList = new ArrayList<DisplayCourse>();

        if (courseList == null) {
            context.addMessage(new MessageBuilder().info().code("units.empty")
                    .build());
        } else {
            for (Course course : courseList) {
                displayCourseList.add(new DisplayCourse(course));
            }
        }
        return displayCourseList;
    }

    /**
     * @see fr.umlv.m2.jee.spij.service.course.IDisplayCourseService#getAllDisplayCoursesByTrainingName(String,
     *      MessageContext)
     */
    @Transactional(readOnly = true)
    @Override
    public List<DisplayCourse> getAllDisplayCoursesByTrainingName(
            String trainingName, MessageContext context) {

        Training training = trainingDAO.findByName(trainingName);
        List<Course> courseList = training.getCourses();
        final List<DisplayCourse> displayCourseList = new ArrayList<DisplayCourse>();

        if (courseList.isEmpty()) {
            context.addMessage(new MessageBuilder().error()
                    .code("courses.empty").build());
        } else {
            for (Course course : courseList) {
                DisplayCourse displayCourse = new DisplayCourse(course);
                if (courseList != null) {
                    displayCourse.setTraining(training.getId());
                }
                displayCourseList.add(displayCourse);
            }
        }

        return displayCourseList;
    }

    /**
     * (non-Javadoc)
     *
     * @see fr.umlv.m2.jee.spij.service.course.IDisplayCourseService#createCourse(DisplayCourse,
     *      MessageContext)
     */
    @Override
    @Transactional(readOnly = false)
    public boolean createCourse(final DisplayCourse displayCourse,
                                final long trainningId, final MessageContext context) {

        final Course course = new Course();

        List<String> obligatoryUnits = displayCourse.getObligatoryUnits();

        List<Unit> listObligatoryUnits = new ArrayList<Unit>();

        Iterator<String> itListItem = obligatoryUnits.iterator();
        while (itListItem.hasNext()) {
            String item = itListItem.next();
            Unit unit = unitDAO.findByName(item);
            listObligatoryUnits.add(unit);
        }

        List<String> optionalUnits = displayCourse.getOptionalUnits();

        List<Unit> listOptionalUnits = new ArrayList<Unit>();

        Iterator<String> itListItemOp = optionalUnits.iterator();
        while (itListItemOp.hasNext()) {
            String item = itListItemOp.next();
            Unit unit = unitDAO.findByName(item);
            listOptionalUnits.add(unit);
        }

        course.setTraining(trainningId);

        if (!displayCourse.getName().isEmpty()) {
            course.setName(displayCourse.getName());
        }
        if (displayCourse.getNbMatter() > 0) {
            course.setNbMatter(displayCourse.getNbMatter());
        } else {

            // TODO validator dans la page plutot qu'ici ?
            context.addMessage(new MessageBuilder().error()
                    .code("course.NbMatter.positive").build());
            return false;
        }

        // Checks whether a unit already exists
        for (Course persistCourse : courseDAO.findAll()) {
            if (persistCourse.getName().equals(course.getName())) {
                context.addMessage(new MessageBuilder().error()
                        .arg(course.getName()).code("course.name.unique")
                        .build());
                return false;
            }
        }

        if (listObligatoryUnits.size() != 0) {
            course.setRequiredUnits(listObligatoryUnits);
        } else {
            context.addMessage(new MessageBuilder().error()
                    .code("units.obligatory.notNull").build());
            return false;
        }
        course.setOptionalUnits(listOptionalUnits);

        if (listObligatoryUnits.size() + listOptionalUnits.size() > course
                .getNbMatter()) {
            context.addMessage(new MessageBuilder().error()
                    .code("units.choose").build());
            return false;
        }

        if (listObligatoryUnits.size() != 0 && listOptionalUnits.size() != 0) {
            Iterator<Unit> it = listObligatoryUnits.iterator();
            while (it.hasNext()) {
                Unit unit = (Unit) it.next();
                if (listOptionalUnits.contains(unit)) {
                    context.addMessage(new MessageBuilder().error()
                            .arg(unit.getName()).code("units.ambiguous")
                            .build());
                    return false;
                }
            }
        }
        // persists the unit into the db and displays the valuable message
        // context
        courseDAO.persist(course);
        context.addMessage(new MessageBuilder().info().arg(course.getName())
                .code("course.persist").build());

        return true;
    }

    /**
     * @see fr.umlv.m2.jee.spij.service.course.IDisplayCourseService#deleteCourseById(long,
     *      MessageContext)
     */
    @Override
    @Transactional(readOnly = false)
    public boolean deleteCourseById(long id, final MessageContext context) {
        boolean hasBeenRemoved = false;
        final Course course = courseDAO.findById(id);

        if (course != null) {
            context.addMessage(new MessageBuilder().info()
                    .arg(course.getName()).code("course.delete").build());
            courseDAO.remove(course);
            hasBeenRemoved = true;
        }

        return hasBeenRemoved;
    }

    /**
     * @see fr.umlv.m2.jee.spij.service.course.IDisplayCourseService#updateCourseById(DisplayCourse,
     *      MessageContext)
     */
    @Override
    @Transactional(readOnly = false)
    public boolean updateCourseById(final DisplayCourse modifiedCourse,
                                    final MessageContext context) {
        boolean hasBeenUpdated = false;

        final Course toMerge = courseDAO.findById(modifiedCourse.getId());

        // TODO better validators
        if (modifiedCourse.getNbMatter() <= 0) {
            context.addMessage(new MessageBuilder().error()
                    .code("course.NbMatter.positive").build());
            return false;
        }

        if (toMerge != null) {
            if (!toMerge.getName().equals(modifiedCourse.getName())) {
                context.addMessage(new MessageBuilder()
                        .info()
                        .args(new Object[]{toMerge.getName(),
                                modifiedCourse.getName()})
                        .code("course.update.name").build());
                toMerge.setName(modifiedCourse.getName());
                hasBeenUpdated = true;
            }
            if (toMerge.getNbMatter() != modifiedCourse.getNbMatter()) {
                context.addMessage(new MessageBuilder()
                        .info()
                        .args(new Object[]{toMerge.getNbMatter(),
                                modifiedCourse.getNbMatter()})
                        .code("course.update.nbMatter").build());
                toMerge.setNbMatter(modifiedCourse.getNbMatter());
                hasBeenUpdated = true;
            }

            courseDAO.merge(toMerge);
        }

        if (!hasBeenUpdated) {
            context.addMessage(new MessageBuilder().error()
                    .defaultText("update failed").build());
        }

        return hasBeenUpdated;
    }

}
